又閱讀了兩個章節之後,到了愉快的考試時間!分享一件有趣的事情:在這次的考題中,AI 居然給出了錯誤的解答。看來除了靠科技輔助,識別對錯的學習能力依然重要呢。
Q1. 以下是一個使用 factory function 來建立 YoyoFamily 物件的函式:
void f()
{
YoyoFamily* pInv = createYoyoFamily();
// use pInv here...
delete pInv;
}
這種資源管理方式的主要問題是什麼?
A) 在 C++ 中無法使用指標搭配工廠函式。
B) 此程式碼無法編譯,因為 delete
不能用在指標上。
C) 在某些情境下資源將無法釋放,導致記憶體洩漏。
D) delete
只能用於陣列,不能用於單一物件。
E) 在刪除指標時,函式將永遠拋出 exception。
如果在執行到
delete
之前,函式因為return
、exception 或其他控制流程而提早結束,那麼動態配置的資源就無法被釋放,從而造成記憶體洩漏。
這是一個典型的資源管理問題,通常會透過 RAII 來解決。透過物件的生命週期來管理資源,確保資源在離開作用域時自動釋放。
Q2. 比直接在函式呼叫中傳遞 new Widget
,為何更推薦以下程式碼的寫法?
std::tr1::shared_ptr<Widget> pw(new Widget);
processWidget(pw, priority());
A) 讓程式碼更加精簡,增加可讀性。
B) 可以將 raw pointer 自動轉換成 shared_ptr
等智慧指標。
C) 在進行可能會丟出 exception 的其他操作之前,能避免資源洩漏。
D) 避免使用動態記憶體配置。
E) 保證 priority()
一定會先被呼叫。
在獨立的程式碼中先使用
shared_ptr
等智慧指標來儲存new
配置的物件,可以確保這個資源在進行其他可能會丟出 exception 的操作之前,已經被妥善管理。這樣就能有效避免資源洩漏的問題,是 C++ 中良好的 exception safety 實作方式之一。
在 知識點 17 中曾經提到過,函式參數的求值順序是未定義的。以這題來說,priority()
可能會在 shared_ptr
建構之前執行。然而自 C++ 17 之後的版本,函式呼叫參數的求值順序已明確定義為從左至右。只是為了保險起見,仍建議將資源管理與其他操作分開寫。
Q1. 為什麼在一個 type-safe 的 Month
類別中表示特定月份,使用像是 Month::Jan()
的函式會比靜態物件 Month::January
更好?
A) 函式比物件執行得更快。
B) 非區域靜態物件的初始化可能會有問題。
C) 函式都允許隱式轉換,可提升程式碼的可讀性。
D) 在 C++ 中,enum
永遠是 type-safe 的。
E) 因為物件不能用作函式引數。
使用像是
Month::Jan()
的函式可以避免在 C++ 中與初始化順序相關的異常,這確保了Month
物件在需要時能夠可靠地建構。
在 C++ 中,全域變數和靜態變數等靜態物件會在程式啟動時初始化,並在結束時銷毀。靜態物件的初始化順序是有規範的,但這個順序對於不同的 translation unit 是未定義的。這可能導致某些靜態物件未按預期的順序被初始化,尤其是在複數檔案之間依賴靜態物件時更為嚴重。
Q2. 為什麼即使在兩者提供相同功能時,C++ 仍然偏好使用非成員非友元函式而不是成員函式?
A) 非成員非友元函式能增加封裝性。
B) 非成員函式比成員函式執行得更快。
C) 非成員函式只能在全域範圍內定義。
D) 成員函式無法呼叫其他成員函式。
E) 非成員函式會自動成為類別的友元。
非成員非友元函式無法訪問類別的私有成員,因此不會降低封裝性。而成員函式可以訪問所有的私有成員,即使是新增的成員函式依舊能訪問私有資料。非成員函式可以定義在
namespace
中,且不會被預設為友元。成員函式之間則可以相互呼叫。
「友元」就是知識點 24 所說的 friend
。這是 C++ 的一個特殊概念,允許特定的函式、類別或方法訪問另一個類別的私有成員和保護成員。通常,私有成員和保護成員是類別內部使用的,不會對外暴露。但透過將其他類別或函式設為友元可以突破封裝性。